package rewind.show.es.connection;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContextBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

/**
 *
 * 8.x 版本默认启用 HTTPS 和身份验证，需配置证书、用户名密码等信息。
 * Elasticsearch 8.x 启动时会自动生成 CA 证书（$ES_HOME/config/certs/http_ca.crt），将其复制到项目的 src/main/resources 目录下，用于客户端验证服务器身份。
 */
@Configuration
@ConfigurationProperties(prefix = "rewind.es")
public class EsClientConfig {

    /**
     * ES 集群地址（多个用逗号分隔）
     */
    private String uris;

    private String username;

    private String password;

    /**
     * 信任的 CA 证书（需从 ES 集群获取）
     */
    private String caCertPath;

    @Bean
    public RestClient restClient() throws Exception {
        // 解析 ES 地址（支持多个节点）
        String[] hosts = uris.split(",");
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for (int i = 0; i < hosts.length; i++) {
            String host = hosts[i].replace("https://", "").replace("http://", "");
            String[] parts = host.split(":");
            httpHosts[i] = new HttpHost(parts[0], Integer.parseInt(parts[1]), "https");
        }

        // 配置身份验证
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(username, password));

        SSLContext sslContext = null;
        if (!StringUtils.isEmpty(caCertPath)) {
            // 加载 CA 证书，信任 ES 服务器
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            InputStream certStream = getClass().getResourceAsStream(caCertPath.replace("classpath:", "/"));
            X509Certificate cert = (X509Certificate) factory.generateCertificate(certStream);

            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            trustStore.setCertificateEntry("es-ca", cert);

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);

            sslContext = SSLContextBuilder.create()
                    .loadTrustMaterial(trustStore, null)
                    .build();
        }
        SSLContext finalSslContext = sslContext;

        // 构建 RestClient
        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    if (finalSslContext != null) {
                        httpClientBuilder.setSSLContext(finalSslContext);
                    }
                    return httpClientBuilder;
                });

        return restClientBuilder.build();
    }

    @Bean
    public ElasticsearchClient elasticsearchClient(RestClient restClient) {

        // 创建传输层（使用 Jackson 处理 JSON）
        ElasticsearchTransport transport = new RestClientTransport(
            restClient, new JacksonJsonpMapper()
        );

        return new ElasticsearchClient(transport);
    }
}
